/*
 * Package il.ac.biu.cs.grossmm.impl.activeData
 * File Discriminator.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.impl.activeData;

import il.ac.biu.cs.grossmm.api.keys.Attribute;
import il.ac.biu.cs.grossmm.api.keys.Key;
import il.ac.biu.cs.grossmm.api.keys.KeyPattern;
import il.ac.biu.cs.grossmm.api.keys.PatternEntry;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Discriminator {

	static Map<Class, String> classToDbType;

	static {
		classToDbType = new HashMap<Class, String>();
		classToDbType.put(String.class, "VARCHAR");
		classToDbType.put(Integer.class, "INTEGER");
		classToDbType.put(Double.class, "DOUBLE");
		classToDbType.put(Float.class, "FLOAT");
		classToDbType.put(Boolean.class, "BOOLEAN");
		classToDbType.put(Short.class, "SMALLINT");
		classToDbType.put(Byte.class, "TINYINT");
		// classToDbType.put(Byte[].class, "VARBINARY");
	}

	public static class Entry implements Comparable<Entry> {
		final Attribute[] attributes;

		final Class clazz;

		final String dbType;

		Entry(Class clazz) {
			attributes = null;
			this.clazz = clazz;
			this.dbType = classToDbType.get(clazz);
		}

		Entry(Attribute[] attributes, Class clazz) {
			this.attributes = attributes;
			this.clazz = clazz;
			this.dbType = classToDbType.get(clazz);
		}

		void setAttribute(int n, Attribute attribute) {
			attributes[n] = attribute;
		}

		public int compareTo(Entry o) {
			return clazz.getName().compareTo(o.clazz.getName());
		}

		Attribute[] getAttributes() {
			return attributes;
		}

		Class<?> getValueClass() {
			return clazz;
		}

		Object getValue(Key key) {
			for (int i = 0; i < attributes.length; i++) {
				key = key.subkey(attributes[i]);
			}

			return key.value();
		}

		public String getDbType() {
			return dbType;
		}
	}

	protected List<Entry> entries;

	public Discriminator(KeyPattern pattern) {
		if (pattern.isMask())
			throw new Error("Masked patterns not allowed");

		entries = new ArrayList<Entry>();

		List<Attribute> attributes = new ArrayList<Attribute>();

		peek(pattern, attributes);

		Collections.sort(entries);

		entries = Collections.unmodifiableList(entries);
	}

	void peek(KeyPattern pattern, List<Attribute> attributes) {
		if (pattern.isMask())
			throw new Error("Masked patterns not allowed");

		Class valueClass = pattern.valueClass();

		if (valueClass != null) {
			Attribute[] asArray = new Attribute[attributes.size()];
			attributes.toArray(asArray);
			entries.add(new Entry(asArray, valueClass));
		}

		int size = pattern.size();
		int attSize = attributes.size();

		for (int i = 0; i < size; i++) {
			PatternEntry patternEntry = pattern.entry(i);

			Attribute attribute = patternEntry.getAttribute();
			KeyPattern nestedPattern = patternEntry.getPattern();

			attributes.add(attribute);

			peek(nestedPattern, attributes);

			attributes.remove(attSize);
		}
	}

	/**
	 * @return the entries
	 */
	public List<Entry> getEntries() {
		return entries;
	}

	public Entry get(int i) {
		return entries.get(i);
	}

	public int size() {
		return entries.size();
	}

	public void populate(Key key, PreparedStatement ps, int fromIndex)
			throws SQLException {
		int i = fromIndex;

		for (Entry e : entries) {
			Object o = e.getValue(key);
			Class clazz = e.getValueClass();

			if (clazz == String.class) {
				ps.setString(i, (String) o);
			} else if (clazz == Integer.class) {
				ps.setInt(i, ((Integer) o).intValue());
			} else if (clazz == Boolean.class) {
				ps.setBoolean(i, ((Boolean) o).booleanValue());
			} else if (clazz == Double.class) {
				ps.setDouble(i, ((Double) o).doubleValue());
			} else if (clazz == Float.class) {
				ps.setInt(i, ((Float) o).intValue());
			} else

				i++;
		}
	}
}
